
package com.gps.gate.database;

import com.gps.common.MessageType;
import com.gps.common.model.BaseModel;
import com.gps.common.model.Device;
import com.gps.gate.GlobalTimer;
import com.gps.gate.Protocol;
import com.gps.gate.trans.PosTrans;
import io.netty.channel.Channel;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.SocketAddress;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Component
public class ConnectionManager {
    private final long deviceTimeout;
    private final boolean enableStatusEvents;

    private final Map<String, ActiveDevice> activeDevices = new ConcurrentHashMap<>();
    private final Map<String, Timeout> timeouts = new ConcurrentHashMap<>();
    private final Map<Channel, ActiveDevice> activeChannels = new ConcurrentHashMap<>();

    @Autowired
    private CommandsManager commandsManager;
    @Autowired
    private PosTrans posTrans;

    public ConnectionManager() {
        deviceTimeout = 20 /*GpsGateContext.getConfig().getLong("status.timeout", DEFAULT_TIMEOUT)*/ * 1000;
        enableStatusEvents = true;//GpsGateContext.getConfig().getBoolean("event.enable");
    }

    public ActiveDevice addActiveDevice(String imei, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
        ActiveDevice device = new ActiveDevice(imei, protocol, channel, remoteAddress);
        //判断是否存在连接断开未清除session的设备
        for (ActiveDevice activeDevice : activeChannels.values()) {
            if (imei.equals(activeDevice.getImei())) {
                activeChannels.remove(activeDevice.getChannel());
            }
        }
        //更新imei与在线设备的对应关系
        activeDevices.put(imei, device);
        //更新当前在线设备与channel的对应
        activeChannels.put(channel, device);
        return device;
    }

    public ActiveDevice removeActiveDevice(Channel channel) {
        ActiveDevice activeDevice = activeChannels.remove(channel);
        //当前通道不存在，新通道已建立，已经移除原有通道
        if (activeDevice == null) {
            return null;
        }

        activeDevices.remove(activeDevice.getImei());

        return activeDevice;
    }

    public boolean removeActiveDevice(String imei) {
        ActiveDevice activeDevice = activeDevices.remove(imei);
        if (activeDevice != null) {
            Channel channel = activeDevice.getChannel();
            activeChannels.remove(channel);
            return true;
        }
        return false;
    }

    public ActiveDevice getActiveDevice(String imei) {
        if (imei == null) {
            return null;
        }
        return activeDevices.get(imei);
    }

    public ActiveDevice getActiveDevice(Channel channel) {
        if (channel == null) {
            return null;
        }
        return activeChannels.get(channel);
    }

    public void updateDevice(final String imei, String status) {
        ActiveDevice device = getActiveDevice(imei);
        if (device == null) {
            return;
        }
        String oldStatus = device.getStatus();
        device.setStatus(status);
        if (enableStatusEvents && !status.equals(oldStatus)) {
            BaseModel baseModel = new BaseModel();
            baseModel.setServerTime(new Date());
            baseModel.setImei(imei);
            switch (status) {
                case Device.STATUS_ONLINE:
                    baseModel.setMessageType(MessageType.TYPE_ONLINE);
                    pushMessage(baseModel);
                    break;
                default:
                    baseModel.setMessageType(MessageType.TYPE_OFFLINE);
                    pushMessage(baseModel);
                    break;
            }
        }

        Timeout timeout = timeouts.remove(imei);
        if (timeout != null) {
            timeout.cancel();
        }
        if (status.equals(Device.STATUS_ONLINE)) {
            timeouts.put(imei, GlobalTimer.getTimer().newTimeout(new TimerTask() {
                @Override
                public void run(Timeout timeout) {
                    if (!timeout.isCancelled()) {
                        updateDevice(imei, Device.STATUS_OFFLINE);
                        if (getActiveDevice(imei) != null) {
                            removeActiveDevice(imei);
                        }
                    }
                }
            }, deviceTimeout, TimeUnit.MILLISECONDS));
        }
    }

    public Collection<ActiveDevice> activeDevices() {
        return activeDevices.values();
    }

    public void pushMessage(BaseModel model) {
        posTrans.send(model);
    }

}
